package stcpscheculer;

import java.util.HashSet;
import java.util.Random;
import java.util.Set;

public class Chromosome {

    private int Buses;
    private int Drivers;
    private int TimeSlot; // in hours
    private int Slots;
    private float adaptation;
    public Driver[] driversArr;
    public Bus[] busesArr;
    //public Set<Integer> Solution;
    private int[] SolutionArr;

    /**
     * Sets Chromosome parameters
     *
     * @param int buses - number of buses
     * @param int drivers - number of drivers
     * @param int timeseg - time segment length to be used
     */
    public Chromosome() {

        Buses = Configuration.getBuses();
        Drivers = Configuration.getDrivers();
        TimeSlot = Configuration.getTimeSlot();
        Slots = Configuration.getSlots();
        driversArr = new Driver[Drivers];
        busesArr = new Bus[Buses];
        
        System.arraycopy(Configuration.busesArr, 0, busesArr, 0, Buses);
        System.arraycopy(Configuration.driversArr, 0, driversArr, 0, Drivers);
        
        SolutionArr = new int[Configuration.getChromosomeLength()];
    }

    /**
     * creates a chromosome for initial population with random data
     *
     */
    public void init() {
        Random generator = new Random();
        // for each driver fill schedule
//        for (int d = 0; d < Drivers; d++) {
//            // for each timeslot choose a bus or zero
//            for (int t = 0; t < Slots; t++) {
//                int bus = generator.nextInt(Buses * 3 + 1); // ensure that driver works 1/3 of the time
//                int position = d * Slots + t;
//                System.out.println("In Init. position = " + position);
//                if (bus > Buses) {
//                    SolutionArr[position] = 0;
//                } else {
//                    SolutionArr[position] = bus;
//                }
//            }
//        }
        for(int i=0; i < Configuration.getChromosomeLength(); i++){
                int bus = generator.nextInt(Buses * 3 + 1); // ensure that driver works 1/3 of the time
                if (bus > Buses) {
                    SolutionArr[i] = 0;
                } else {
                    SolutionArr[i] = bus;
                }
            
        }
    }

    /**
     * Calculates Adaptation function of a solution
     *
     * @return solution's adaptation value
     */
    public float fa() {
        int busesUsed = 0;
        boolean[] busInUse; // true if bus is used, otherwise false
        int[] driverWorkingTimeslots; // how many timeslots driver is working
        int busExpTimeslots = 0; // Drivers timeslots within experience or preference with bus type
        int driversDiff = 0;  // Driver's max work time - actual work time
        int slotsWithinTimePrefs = 0; // timeslots within drivers' time preferences
        int[][] busOverlaps; // how many times bus is used in same timeslot
        int totalOverlaps = 0; // total number of overlapses
        busInUse = new boolean[Buses]; 
        busOverlaps = new int[Buses][Slots];
        driverWorkingTimeslots = new int[Drivers];
        for (int i = 0; i < Buses; i++) {
            busInUse[i] = false;
            for(int t=0; t<Slots; t++)
               busOverlaps[i][t] = 0; 
        }
        for (int i = 0; i < Drivers; i++) {
            driverWorkingTimeslots[i] = 0;
        }
//      fa = (NumeroDasCarreirasUsadas/NumeroDasCarreirasTotal) * f1 
//      - SomaParaCadaMotorista |TimeslotsMaximoDeTrabalho - TimeslotsDeTrabalhoAtual| * f2
//         /NumeroDeTimeslotsTotal
//      + SomaParaCadaMotorista (TimeslotsEmTrabalho onde motorista tem experiência ou/e preferência na carreira atribuída)
//         * f3/NumeroDeTimeslotsTotal
//      + SomaParaCadaMotorista (TimeslotsEmTrabalho dentro das preferências do motorista)
//         * f4 /NumeroDeTimeslotsTotal
//      - (TimeslotsComSobrepostas/NumeroDeTimeslotsTotal )* f5 

        for (int d = 0; d < Drivers; d++) {
            for (int t = 0; t < Slots; t++) {
                int pos = d * Slots + t;
                if (SolutionArr[pos] != 0) {
                    //Mark bus as used
                    busInUse[SolutionArr[pos] - 1] = true;
                    // increment driver's working timeslots
                    driverWorkingTimeslots[d]++;
                    //System.out.println("SolutionArr[pos] = " + SolutionArr[pos]);
                    Bus b = busesArr[SolutionArr[pos]-1];
                    // count working timeslots within driver's bus esperience & preferences
                    if (driversArr[d].BusTypes.contains(b.BusType)
                            || driversArr[d].BusTypesPref.contains(b.BusType)) {
                        busExpTimeslots++;
                    }
                    // count timeslots within driver's time preferences
                    if (driversArr[d].timeslotIsOk(t)) {
                        slotsWithinTimePrefs++;
                    }
                    // count how many times each bus is used in each timeslot
                    busOverlaps[SolutionArr[pos]-1][t]++;
                }

            }
            driversDiff += Math.abs(driversArr[d].MaxWorkTime - driverWorkingTimeslots[d]);
        }
        // count how many buses are used and total number of overlaps
        for (int i = 0; i < Buses; i++) {
            if (busInUse[i]) {
                busesUsed++;
                for(int t = 0; t < Slots; t++){
                    if(busOverlaps[i][t]>1){
                        totalOverlaps+= (busOverlaps[i][t] - 1);
                    }
                }
            }
        }

        adaptation = busesUsed * Configuration.getF1() / Drivers
                - driversDiff * Configuration.getF2() / Configuration.getChromosomeLength()
                + busExpTimeslots * Configuration.getF3() / Configuration.getChromosomeLength()
                + slotsWithinTimePrefs * Configuration.getF4() / Configuration.getChromosomeLength()
                - totalOverlaps * Configuration.getF5() / Configuration.getChromosomeLength();
        return adaptation;
    }

    public float getAdaptation() {
        return adaptation;
    }

    public int[] getSolutionArr() {
        return SolutionArr;
    }
    
    /**
     * Copy a segment to SolutionArr
     * 
     * @param int beginwith - position of SolutionArr from wich to begin copying 
     * @param int segSize - size of the segment to copy
     * @param int[] SolutionToCopy - array to copy
     */
    public void setSolutionArr(int beginwith, int segSize, int[] SolutionToCopy){
        for(int i=beginwith;i<segSize;i++){
            SolutionArr[i]=SolutionToCopy[i];
        }
    }
    
    /**
     * Apply mutation to every position in SolutionArr
     * 
     */
    public void mutate(){
        Random generator = new Random();
        for(int i=0;i<Configuration.getChromosomeLength();i++){
            float mut = generator.nextInt(100)/100;
            if(mut < Configuration.getPmut()){
                SolutionArr[i] = generator.nextInt(Buses + 1);             
            }
            
        }
    }
    

    /**
     * Calculates number of overlaps in a solution (same bus attributed to
     * different drivers at the same time)
     *
     * @return number of overlaps
     */
    public int NumberOfOverlaps() {
        return 0;
    }

    /**
     * Calculates working hours attributed to a driver
     *
     * @param driver - Driver's ID
     * @return
     */
    public int DriverHoursWorking(int driver) {
        return 0;
    }

    /**
     * Calculates working hours attributed to a bus
     *
     * @param bus - Bus ID
     * @return
     */
    public int BusHoursWorking(int bus) {
        return 0;
    }

    /**
     * Detects if a bus is used in the solution
     *
     * @param int bus bus id
     * @return boolean: true if bus is used, false if not
     */
    public boolean BusInUse(int bus) {
        return true;
    }

    /**
     * Calculates the number of buses not in use
     *
     * @return
     */
    public int BusesNotUsed() {
        int result = 0;
        return result;

    }
    
    /**
     * Print Solution
     * 
     */
    public void PrintSolution(){
        System.out.println("*** Solution ***");
        System.out.println("Drivers: " + Drivers + "; Buses: " + Buses);
        for(int i=0; i<Slots; i++){
            System.out.print("Slot " + i + ": ");
            if(SolutionArr[i] != 0)
                System.out.println("Bus " + SolutionArr[i]);
            else System.out.println("-----");
        }
    }
}
